package com.chenfan.finance.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Function;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
public class TrashBagList<T> {
    private String key;
    private BigDecimal sum;
    private List<TrashSum> nextList;
    private TrashBagList nextSum;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public BigDecimal getSum() {
        return sum;
    }

    public void setSum(BigDecimal sum) {
        this.sum = sum;
    }

    public List<TrashSum> getNextList() {
        return nextList;
    }

    public void setNextList(List<TrashSum> nextList) {
        this.nextList = nextList;
    }

    public TrashBagList getNextSum() {
        return nextSum;
    }

    public void setNextSum(TrashBagList nextSum) {
        this.nextSum = nextSum;
    }

    private static class TrashSum {
        private String no;
        private BigDecimal num;

        public String getNo() {
            return no;
        }

        public void setNo(String no) {
            this.no = no;
        }

        public BigDecimal getNum() {
            return num;
        }

        public void setNum(BigDecimal num) {
            this.num = num;
        }
    }

    public List<T> trashBag(List<T> tList, Function<T, Object> noInput, Function<T, Object> numInput, BigDecimal target) {
        if (CollectionUtils.isEmpty(tList)) {
            return new LinkedList<>();
        }
        log.info(JSONObject.toJSONString(tList));
        List<TrashSum> list = new ArrayList<>(tList.size());
        Map<String, T> targetMap = new HashMap<>(16);
        for (T t : tList) {
            TrashSum sum = new TrashSum();
            Object noFieldValue = noInput.apply(t);
            Assert.notNull(noFieldValue, "id 不能为空");
            Object numFieldValue = numInput.apply(t);
            if (numFieldValue == null) {
                continue;
            }
            BigDecimal numIn = new BigDecimal(String.valueOf(numFieldValue));
            if (numIn.compareTo(target) == 0) {
                return Collections.singletonList(t);
            }
            ReflectUtil.setFieldValue(sum, "no", noFieldValue);
            ReflectUtil.setFieldValue(sum, "num", numIn);

            T put = targetMap.put(String.valueOf(noFieldValue), t);
            Assert.isNull(put, "id 重复：" + noFieldValue);
            list.add(sum);
        }
        if (tList.size() < 3) {
            List<T> tListTarget = new ArrayList<>(3);
            if (tList.size() == 2) {
                BigDecimal add = list.get(0).getNum().add(list.get(1).getNum());
                if (add.compareTo(target) == 0) {
                    tListTarget.add(targetMap.get(list.get(0).getNo()));
                    tListTarget.add(targetMap.get(list.get(1).getNo()));
                    return tListTarget;
                }
            }

            for (TrashSum trashSum : list) {
                if (trashSum.getNum().compareTo(target) == 0) {
                    tListTarget.add(targetMap.get(trashSum.getNo()));
                    return tListTarget;
                }
            }

        }
        List<TrashBagList> listSums = firstSum(list);
        String key = null;
        sums:
        for (TrashBagList listSum : listSums) {
            while (listSum != null && listSum.getNextList().size() > 1) {
                if (listSum.getSum().compareTo(target) == 0) {
                    key = listSum.getKey();
                    break sums;
                }
                TrashBagList next = getNextSum(listSum);
                listSum = next;
            }
        }
        if (key == null) {
            return new LinkedList<>();
        }
        String[] nos = key.split(",");
        List<T> tListTarget = new ArrayList<>(nos.length);
        for (String no : nos) {
            tListTarget.add(targetMap.get(no));
        }
        return tListTarget;
    }


    //  public static void main(String[] args) {
    //    BigDecimal target = 4757;
//        List<TrashSum> list = getDta();
//        List<TrashBagList> listSums = test1(list);
//        String key = "";
//        try {
//            Thread.sleep(8000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        sums:
//        for (TrashBagList listSum : listSums) {
//            while (listSum != null && listSum.getNextList().size() > 1) {
//                if (listSum.getSum() == target) {
//                    key = listSum.getKey();
//                    break sums;
//                }
//                TrashBagList next = getnextSum(listSum);
//                // listSum.setNextSum(next);
//                listSum = next;
//            }
//        }
//        System.out.println(key);
//        for (TrashBagList listSum : listSums) {
//            while (listSum != null) {
//                if(listSum.getSum()==target){
//                    key= listSum.getKey();
//                }
//                System.out.print(listSum.getKey() + " = " + listSum.getSum());
//                listSum = listSum.getNextSum();
//                System.out.print("  ->>>> ");
//            }
//            System.out.println("-- ");
//        }
//        System.out.println(key);
//    }


//    private static List<TrashSum> getDta() {
//        List<Integer> list = new ArrayList<>();
//        for (BigDecimal i = 0; i < 500; i++) {
//            list.add(i);
//        }
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        List<TrashSum> trashSumList = new ArrayList<>();
//        for (Integer integer : list) {
//            TrashSum sum = new TrashSum();
//            sum.setNo("no_" + integer + "");
//            sum.setSum(integer);
//            trashSumList.add(sum);
//        }
//        return trashSumList;
//    }

    private TrashBagList getNextSum(TrashBagList listSum) {
        if (listSum.getNextList().size() == 2) {
            List<TrashSum> nextList = listSum.getNextList();
            TrashSum fir = nextList.get(1);
            TrashBagList sum = new TrashBagList();
            sum.setKey(listSum.getKey() + "," + fir.getNo());
            List<TrashSum> listNew = new ArrayList<>();
            TrashSum last = new TrashSum();
            last.setNo(listSum.getKey() + fir.getNo());
            last.setNum(listSum.getSum().add(fir.getNum()));
            listNew.add(last);
            sum.setNextList(listNew);
            sum.setSum(getSum2(listSum.getSum(), fir));
            return sum;
        }
        List<TrashSum> nextList = listSum.getNextList();
        TrashBagList sum = new TrashBagList();
        TrashSum fir = nextList.get(1);
        sum.setKey(listSum.getKey() + "," + fir.getNo());
        List<TrashSum> newList = getNewList(0, 1, nextList);
        sum.setNextList(newList);
        sum.setSum(getSum2(listSum.getSum(), fir));
        return sum;
    }

    private BigDecimal getSum2(BigDecimal sum, TrashSum fir) {
        return sum.add(fir.getNum());
    }


    private List<TrashBagList> firstSum(List<TrashSum> list) {
        List<TrashBagList> listSums = new ArrayList<>();
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = i + 1; j < list.size(); j++) {
                TrashSum first = list.get(i);
                TrashSum second = list.get(j);
                List<TrashSum> newList = getNewList(i, j, list);
                TrashBagList sum = new TrashBagList();
                sum.setKey(first.getNo() + "," + second.getNo());
                sum.setNextList(newList);
                sum.setSum(getSum(first, second));
                listSums.add(sum);
            }
        }
        return listSums;
    }

    private BigDecimal getSum(TrashSum first, TrashSum second) {
        return first.getNum().add(second.getNum());
    }

    private List<TrashSum> getNewList(int i, int j, List<TrashSum> list) {
        List<TrashSum> listNew = new ArrayList<>();
        listNew.add(merge(list.get(i), list.get(j)));
        listNew.addAll(getOther(i, j, list));
        return listNew;
    }


    private TrashSum merge(TrashSum fir, TrashSum sec) {
        TrashSum d = new TrashSum();
        d.setNum(fir.getNum().add(sec.getNum()));
        d.setNo(fir.getNo() + sec.getNo());
        return d;
    }

    private List<TrashSum> getOther(int first, int sec, List<TrashSum> list) {
        List<TrashSum> other = new ArrayList<>();
        for (int z = 0; z < list.size(); z++) {
            if (z == first || z == sec) {
                continue;
            }
            other.add(list.get(z));
        }
        return other;
    }

}
